#include "soft_iic.h"
#include "delay.h"

#define US 10

/**
 * @brief Initialize soft IIC
 *
 * @param none
 * @return none
 */
int8_t soft_iic_init(void)
{
	return 0;
}

/**
 * @brief Deinitialize soft IIC
 *
 * @param none
 * @return none
 */
int8_t soft_iic_deinit(void)
{
	return 0;
}

/**
 * @brief Start soft IIC communication
 *
 * @param none
 * @return none
 */
void soft_iic_start(void)
{
	SCL(1); // Set SCL high
	SDA(1); // Set SDA high
	delay_us(10);

	SDA(0); // Pull SDA low first
	delay_us(10);

	SCL(0); // Then pull SCL low
	delay_us(10);
}

/**
 * @brief Stop soft IIC communication
 *
 * @param none
 * @return none
 */
void soft_iic_stop(void)
{
	SDA(0); // Set SDA low
	SCL(0); // Set SCL low
	delay_us(10);

	SCL(1); // Pull SCL high first
	delay_us(10);

	SDA(1); // Then pull SDA high
	delay_us(10);
}

/**
 * @brief Send ACK/NACK signal via soft IIC
 *
 * @param ack - 1 for ACK, 0 for NACK
 * @return none
 */
void soft_iic_send_ack(uint8_t ack)
{
	SCL(0); // Set SCL low

	if (ack)
	{
		SDA(1); // Set SDA high
	}
	else
	{
		SDA(0); // Set SDA low
	}

	delay_us(10);
	SCL(1); // Set SCL high
	delay_us(10);
	SCL(0); // Set SCL low
	SDA(1); // Set SDA high
}

/**
 * @brief Wait for ACK/NACK signal via soft IIC (0 for ACK)
 *
 * @param none
 * @return int8_t - 0 for ACK, 1 for NACK
 */
int8_t soft_iic_wait_ack(void)
{
	uint8_t temp = 0;
	SCL(0); // Set SCL low
	SDA(1); // Set SDA high, release SDA
	delay_us(10);

	SCL(1); // Set SCL high
	delay_us(10);

	if (HAL_GPIO_ReadPin(SDA_Port, SDA_PIN))
	{
		temp = 1;
	}
	SCL(0); // Set SCL low

	return temp;
}

/**
 * @brief Send one byte via soft IIC
 *
 * @param data - byte to send
 * @return none
 */
void soft_iic_send_byte(uint8_t data)
{
	uint8_t i;
	for (i = 0; i < 8; i++)
	{
		SDA(data & (0x80 >> i));
		delay_us(10);

		SCL(1); // Set SCL high
		delay_us(10);
		SCL(0); // Set SCL low
	}
}

/**
 * @brief Read one byte via soft IIC
 *
 * @param none
 * @return uint8_t - byte read
 */
uint8_t soft_iic_read_byte(void)
{
	uint8_t data = 0x00;
	uint8_t i;

	SCL(0); // Set SCL low
	delay_us(10);
	SDA(1); // Set SDA high, release SDA

	for (i = 0; i < 8; i++)
	{
		SCL(0); // Set SCL low
		delay_us(10);
		SCL(1); // Set SCL high
		delay_us(10);

		if (HAL_GPIO_ReadPin(SDA_Port, SDA_PIN))
			data |= (0x80 >> i);
	}
	SCL(0); // Set SCL low
	delay_us(10);

	return data;
}

/**
 * @brief Write multiple data via soft IIC
 *
 * @param register_addr - register address
 * @param data - pointer to data buffer
 * @param len - length of data
 * @return int8_t - 0 for success, negative for error
 */
int8_t soft_iic_write_bytes(uint8_t register_addr, uint8_t *data, uint32_t len)
{
	soft_iic_start();
	soft_iic_send_byte(ADDR);	  // Write device address
	if (0 != soft_iic_wait_ack()) // Wait for ACK (1 for NACK)
	{
		soft_iic_stop();
		return -1;
	}
	soft_iic_send_byte(register_addr); // Write register
	if (0 != soft_iic_wait_ack())	   // Wait for ACK (1 for NACK)
	{
		soft_iic_stop();
		return -2;
	}
	while (len--)
	{
		soft_iic_send_byte(*data++);  // Write data
		if (0 != soft_iic_wait_ack()) // Wait for ACK (1 for NACK)
		{
			soft_iic_stop();
			return -3;
		}
	}
	soft_iic_stop();
	return 0;
}

/**
 * @brief Read multiple data via soft IIC
 *
 * @param register_addr - register address
 * @param data - pointer to data buffer
 * @param len - length of data
 * @return int8_t - 0 for success, negative for error
 */
int8_t soft_iic_read_bytes(uint8_t register_addr, uint8_t *data, uint32_t len)
{
	soft_iic_start();
	soft_iic_send_byte(ADDR);	  // Write device address
	if (0 != soft_iic_wait_ack()) // Wait for ACK (1 for NACK)
	{
		soft_iic_stop();
		return -1;
	}
	soft_iic_send_byte(register_addr); // Write register address
	if (0 != soft_iic_wait_ack())	   // Wait for ACK (1 for NACK)
	{
		soft_iic_stop();
		return -2;
	}

	soft_iic_start();				 // Restart sequence
	soft_iic_send_byte(ADDR | 0x01); // Set to read mode
	if (0 != soft_iic_wait_ack())	 // Wait for ACK (1 for NACK)
	{
		soft_iic_stop();
		return -3;
	}
	while (len--)
	{
		*data++ = soft_iic_read_byte();
		if (len > 0)
		{
			soft_iic_send_ack(0); // Send ACK signal
		}
		else
		{
			soft_iic_send_ack(1); // Send NACK signal
		}
	}
	soft_iic_stop();
	return 0;
}